﻿//-----------------------------------------------------------------------
// <copyright file="StringExtensions.cs" company="CodePlex">
//     Copyright (c) Doug Holland, CodePlex. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace CodePlex.Diagnostics.Silverlight
{
    using System;

    using System.Collections;
    using System.Collections.Generic;

    using System.Diagnostics;

    using System.Reflection;

    using System.Security;
    using System.Security.Principal;

    using System.Threading;

    using System.Windows;

    using System.Xml;
    using System.Xml.Linq;
    
    using CodePlex.Diagnostics.Silverlight.Contracts;
    
    /// <summary>
    /// Provides extension methods for the <see cref="T:System.String"/> class to enable the writing of log entries using the CodePlex.Diagnostics framework.
    /// </summary>
    public static class StringExtensions
    {
        #region public static Guid Log(this string entry, LoggingPriority priority, LoggingType type)

        /// <summary>
        /// Writes the <see cref="T:System.String"/> using the configured logging provider.
        /// </summary>
        /// <param name="entry">The <see cref="T:System.String"/> to be logged.</param>
        /// <param name="priority">The <see cref="T:CodePlex.Diagnostics.LoggingPriority"/> of the log entry.</param>
        /// <param name="type">The <see cref="T:CodePlex.Diagnostics.LoggingType"/> of the log entry.</param>
        /// <returns>A <see cref="T:System.Guid"/> representing the unique publication identifier for the log entry.</returns>
        [DebuggerStepThrough]
        public static Guid Log(this string entry, LoggingPriority priority, LoggingType type)
        {
            // determine the security context of the Silverlight application.
            ISecurityContext<IPrincipal> context = Application.Current as ISecurityContext<IPrincipal>;

            if (context == null || context.CurrentPrincipal == null)
            {
                // unable to determine the security context of the Silverlight application.
                throw new SecurityContextException();
            }

            return Log(entry, priority, type, context.CurrentPrincipal.Identity);
        }

        #endregion

        #region public static Guid Log(this string entry, LoggingPriority priority, LoggingType type, IIdentity identity)

        /// <summary>
        /// Writes the <see cref="T:System.String"/> using the configured logging provider.
        /// </summary>
        /// <param name="entry">The <see cref="T:System.String"/> to be logged.</param>
        /// <param name="priority">The <see cref="T:CodePlex.Diagnostics.LoggingPriority"/> of the log entry.</param>
        /// <param name="type">The <see cref="T:CodePlex.Diagnostics.LoggingType"/> of the log entry.</param>
        /// <param name="identity">The <see cref="T:System.Security.Principal.IIdentity"/> to associate with the log entry.</param>
        /// <returns>A <see cref="T:System.Guid"/> representing the unique publication identifier for the log entry.</returns>
        [DebuggerStepThrough]
        public static Guid Log(this string entry, LoggingPriority priority, LoggingType type, IIdentity identity)
        {
            return entry.Log(priority, type, null, identity);
        }

        #endregion

        #region public static Guid Log(this string entry, LoggingPriority priority, LoggingType type, Dictionary<string, string> dictionary)

        /// <summary>
        /// Writes the <see cref="T:System.String"/> using the configured logging provider.
        /// </summary>
        /// <param name="entry">The <see cref="T:System.String"/> to be logged.</param>
        /// <param name="priority">The <see cref="T:CodePlex.Diagnostics.LoggingPriority"/> of the log entry.</param>
        /// <param name="type">The <see cref="T:CodePlex.Diagnostics.LoggingType"/> of the log entry.</param>
        /// <param name="dictionary">A <see cref="T:System.Collections.Generic.Dictionary`2"/> containing name value pairs associated with this log entry.</param>
        /// <returns>A <see cref="T:System.Guid"/> representing the unique publication identifier for the log entry.</returns>
        [DebuggerStepThrough]
        public static Guid Log(this string entry, LoggingPriority priority, LoggingType type, Dictionary<string, string> dictionary)
        {
            return entry.Log(priority, type, dictionary, null);
        }

        #endregion

        #region public static Guid Log(this string entry, LoggingPriority priority, LoggingType type, Dictionary<string, string> dictionary, IIdentity identity)

        /// <summary>
        /// Writes the <see cref="T:System.String"/> using the configured logging provider.
        /// </summary>
        /// <param name="entry">The <see cref="T:System.String"/> to be logged.</param>
        /// <param name="priority">The <see cref="T:CodePlex.Diagnostics.LoggingPriority"/> of the log entry.</param>
        /// <param name="type">The <see cref="T:CodePlex.Diagnostics.LoggingType"/> of the log entry.</param>
        /// <param name="dictionary">A <see cref="T:System.Collections.Generic.Dictionary`2"/> containing name value pairs associated with this log entry.</param>
        /// <param name="identity">The <see cref="T:System.Security.Principal.IIdentity"/> to associate with the log entry.</param>
        /// <returns>A <see cref="T:System.Guid"/> representing the unique publication identifier for the log entry.</returns>
        [DebuggerStepThrough]
        public static Guid Log(this string entry, LoggingPriority priority, LoggingType type, Dictionary<string, string> dictionary, IIdentity identity)
        {
            return entry.Log(priority, type, dictionary, null, identity);
        }

        #endregion

        #region public static Guid Log(this string entry, LoggingPriority priority, LoggingType type, Dictionary<string, string> dictionary, XElement element, IIdentity identity)

        /// <summary>
        /// Writes the <see cref="T:System.String"/> using the configured logging provider.
        /// </summary>
        /// <param name="entry">The <see cref="T:System.String"/> to be logged.</param>
        /// <param name="priority">The <see cref="T:CodePlex.Diagnostics.LoggingPriority"/> of the log entry.</param>
        /// <param name="type">The <see cref="T:CodePlex.Diagnostics.LoggingType"/> of the log entry.</param>
        /// <param name="dictionary">A <see cref="T:System.Collections.Generic.Dictionary`2"/> containing name value pairs associated with this log entry.</param>
        /// <param name="element">A <see cref="T:System.Xml.Linq.XElement"/> representing an xml element to be associated with this log entry.</param>
        /// <param name="identity">The <see cref="T:System.Security.Principal.IIdentity"/> to associate with the log entry.</param>
        /// <returns>A <see cref="T:System.Guid"/> representing the unique publication identifier for the log entry.</returns>
        [DebuggerStepThrough]
        public static Guid Log(this string entry, LoggingPriority priority, LoggingType type, Dictionary<string, string> dictionary, XElement element, IIdentity identity)
        {
            // establish a new publication Id for the log entry.
            Guid publicationId = Guid.NewGuid();

            // establish an identity data contract based upon the supplied identity object.
            IIdentityDataContract identityDataContract = new IIdentityDataContract(identity);

            // establish the log entry data contract.
            LogEntryDataContract logEntryDataContract = new LogEntryDataContract(entry, priority, type, publicationId);

            // unable to access application domain information within Silverlight runtime.
            logEntryDataContract.AppDomain = null;

            StackTrace stackTrace = new StackTrace();

            Assembly assembly = null;

            for (int index = stackTrace.FrameCount - 1; index >= 0; index--)
            {
                StackFrame frame = stackTrace.GetFrame(index);

                MethodBase method = frame.GetMethod();

                if (method.DeclaringType.Assembly != typeof(StringExtensions).Assembly)
                {
                    assembly = method.DeclaringType.Assembly;
                }
            }

            // establish the assembly data contract based upon the calling assembly, that is the assembly containing the method calling this method.
            logEntryDataContract.Assembly = new AssemblyDataContract(assembly);

            // establish the machine data contract.
            logEntryDataContract.Machine = new MachineDataContract();

            // unable to access process information within Silverlight runtime.
            logEntryDataContract.Process = null;

            // establish the thread data contract based upon the current thread.
            logEntryDataContract.Thread = new ThreadDataContract(Thread.CurrentThread);

            // establish a proxy to the diagnostics service.
            DiagnosticsServiceClient client = null;

            try
            {
                // establish a proxy to the diagnostics service.
                client = new DiagnosticsServiceClient();

                // publish the exception using the diagnostics service, returning the publication identifier.
                client.LogAsync(logEntryDataContract, dictionary, element, identityDataContract);

                return publicationId;
            }
            catch
            {
                // causes the client to transition immediately from its current state into the closed state.
                if (client != null)
                {
                    client.Abort();
                }

                return Guid.Empty;
            }
        }

        #endregion
    }
}